/**
 * GWT Kad : Kademlia implementation for GWT
 * Copyright (C) 2012  Pierrick HYMBERT, Paul-André MISTRAL, Florian ALEXANDRE
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.kad.gwt.web.client.kademelia.model.rpc;

import org.jsonmaker.gwt.client.Jsonizer;
import org.jsonmaker.gwt.client.JsonizerParser;
import org.kad.gwt.web.client.kademelia.model.rpc.find.node.KadRpcFindNodeRequestJsonizer;
import org.kad.gwt.web.client.kademelia.model.rpc.find.node.KadRpcNodesFoundResponseJsonizer;
import org.kad.gwt.web.client.kademelia.model.rpc.ping.KadRpcPingRequestJsonizer;
import org.kad.gwt.web.client.kademelia.model.rpc.ping.KadRpcPongResponseJsonizer;
import org.kad.gwt.web.client.kademelia.model.rpc.store.KadRpcStoreRequestJsonizer;
import org.kad.gwt.web.client.kademelia.model.rpc.store.KadRpcStoreResponseJsonizer;

import com.google.gwt.core.client.GWT;

/**
 * The base rpc methods of kademlia.
 * 
 * @author Pierrick HYMBERT &lt;pierrick [dot] hymbert [at] gmail [dot] com&gt;
 */
public enum KadRpcMesageType {
    /**
     * This RPC involves one node sending a PING message to another, which presumably replies with a PONG. This has a two-fold effect: the recipient of the PING must update the bucket corresponding to the sender; and, if there is a reply, the sender
     * must update the bucket appropriate to the recipient. All RPC packets are required to carry an RPC identifier assigned by the sender and echoed in the reply. This is a quasi-random number of length B (160 bits).
     */
    PING(KadRpcPingRequestJsonizer.class, KadRpcPongResponseJsonizer.class),
    /**
     * The sender of the STORE RPC provides a key and a block of data and requires that the recipient store the data and make it available for later retrieval by that key. This is a primitive operation, not an iterative one.
     */
    STORE(KadRpcStoreRequestJsonizer.class, KadRpcStoreResponseJsonizer.class),
    /**
     * The FIND_NODE RPC includes a 160-bit key. The recipient of the RPC returns up to k triples (IP address, port, nodeID) for the contacts that it knows to be closest to the key. The recipient must return k triples if at all possible. It may only
     * return fewer than k if it is returning all of the contacts that it has knowledge of. This is a primitive operation, not an iterative one.
     */
    FIND_NODE(KadRpcFindNodeRequestJsonizer.class, KadRpcNodesFoundResponseJsonizer.class);
    /**
     * 
     */
    // FIND_VALUE;
    /**
     * The type of the jsonizer of the request.
     */
    private final Class<? extends Jsonizer> requestJsonType;
    /**
     * The type of the jsonizer of the response.
     */
    private final Class<? extends Jsonizer> responseJsonType;

    /**
     * Constructor.
     * 
     * @param requestJsonType
     *            {@link #requestJsonType}
     * @param responseJsonType
     *            {@link #responseJsonType}
     */
    KadRpcMesageType(final Class<? extends Jsonizer> requestJsonType, final Class<? extends Jsonizer> responseJsonType) {
        this.requestJsonType = requestJsonType;
        this.responseJsonType = responseJsonType;
    }

    /**
     * Read a request payload.
     * 
     * @param request
     *            The request to decode
     * @return The associated request
     */
    public <T extends KadRpcRequest> T readRequest(String request) {
        return read(requestJsonType, request);
    }

    /**
     * Read a response payload.
     * 
     * @param response
     *            The response to decode
     * @return The associated response
     */
    public <T extends KadRpcResponse> T readResponse(String response) {
        return read(responseJsonType, response);
    }

    /**
     * Read a json string and return an object
     * 
     * @param jsonType
     *            The type of the class
     * @param request
     *            The request
     * @return The decoded json string
     */
    private <T extends KadRpcMessage, J extends Jsonizer> T read(Class<J> jsonType, String request) {
        final J jsonizer = (J) GWT.create(jsonType);
        return (T) JsonizerParser.parse(jsonizer, request);
    }

    /**
     * write a request payload.
     * 
     * @param request
     *            The request to decode
     * @return The associated request
     */
    public <T extends KadRpcRequest> String writeRequest(T instance) {
        return write(requestJsonType, instance);
    }

    /**
     * write a response payload.
     * 
     * @param response
     *            The response to decode
     * @return The associated response
     */
    public <T extends KadRpcResponse> String writeResponse(T instance) {
        return write(responseJsonType, instance);
    }

    /**
     * Generate a json string corresponding to this object
     * 
     * @param jsonType
     *            The type of the class
     * @param instance
     *            The instance to serialize
     * @return The encoded json string
     */
    private <T extends KadRpcMessage, J extends Jsonizer> String write(Class<J> jsonType, T instance) {
        final J jsonizer = (J) GWT.create(jsonType);
        return jsonizer.asString(instance);
    }
}
